package edu.sjtu.grid.workflow.facts;

import edu.sjtu.grid.workflow.Util;

public class FloatingCost extends Cost{
	
		//doubleing
		private double cycle = 0;
		private double cost1 = 0;
		private double time1 = 0;
		private double cost2 = 0;
		private double time2 = 0;

		public FloatingCost(double cycle, double cost1, double time1, double cost2, double time2) {
			this.cycle = cycle;
			this.cost1 = cost1;
			this.time1 = time1;
			this.cost2 = cost2;
			this.time2 = time2;
		}
		
		public double getCycle() {
			return cycle;
		}

		public double getCost1() {
			return cost1;
		}

		public double getTime1() {
			return time1;
		}

		public double getCost2() {
			return cost2;
		}

		public double getTime2() {
			return time2;
		}
		
		public boolean isFixed()
		{
			return false;
		}
		
		public String getPayString() {
			return "doubleing";
		}
		
		public String getCostString() {
			return ""+cycle+" "+cost1+" "+time1+" "+cost2+" "+time2;
		}
		
		@Override
		public double getCheapestCost()
		{
			return Math.min(cost1, cost2);
		}
		
		@Override
		public double getAvgCost() {
			double length2 = time2 - time1;
			double length1 = cycle - length2;
			return (cost1*length1 + cost2*length2)/cycle;
		}
		
		@Override
		public double getNextCheapestTime(double time)
		{
			double nextTime = 0;
			int nCycle = (int)(time / this.cycle);
			time = time - this.cycle * nCycle;
			if(Util.isSmall(cost1, cost2))
			{
				if(Util.isSmall(time, this.time2))
					nextTime = this.time2;
				else 
					nextTime = this.time2 + this.cycle;
			}
			else
			{
				if(Util.isSmall(time, this.time1))
					nextTime = this.time1;
				else 
					nextTime = this.time1 + this.cycle;
			}
			
			return nextTime + this.cycle*nCycle;
		}
		
		private double getNextChangeTime(double time)
		{
			double nextTime = 0;
			int nCycle = (int)(time / this.cycle);
			time = time - this.cycle * nCycle;
			if(Util.isSmall(time, this.time1))
				nextTime = this.time1;
			else if(Util.isSmall(time, this.time2))
				nextTime = this.time2;
			else 
				nextTime = this.time1 + this.cycle;
			
			return nextTime + this.cycle*nCycle;
		}
		
		private double getCost(double fromTime, double toTime)
		{
			double time = (fromTime + toTime) / 2;
			time = time % this.cycle;
			if(Util.isSmall(time, this.time1))
				return this.cost1;
			else if(Util.isSmall(time, this.time2))
				return this.cost2;
			else
				return this.cost1;
		}
		
		@Override
		public double getRealCost(double fromTime, double toTime) {
			double sumCost = 0;
			double t = fromTime;
			while(Util.isSmall(t, toTime))
			{
				double t2 = Math.min(toTime,	this.getNextChangeTime(t));
				double cost = this.getCost(t, t2);
				sumCost += cost * (t2 - t);
				t = t2;
			}
			return sumCost;
		}

		@Override
		public Double getCheapestCost(double from, double to, double duration) {
			if(Util.isBig(from + duration, to))
				return null;
			
			//from
			Double cheapest1 = Double.MAX_VALUE;
			Double cheapestFrom1 = null;
			double time1 = from;
			while(Util.isEqualOrSmall(time1 + duration, to)
					&& Util.isEqualOrSmall(time1 - from, this.cycle))
			{
				double c = this.getRealCost(time1, time1+duration);
				if(Util.isSmall(c, cheapest1))
				{
					cheapest1 = c;
					cheapestFrom1 = time1;
				}
				time1 = this.getNextChangeTime(time1);
			}
			
			//to
			Double cheapest2 = Double.MAX_VALUE;
			Double cheapestFrom2 = null;
			double time2 = from+duration;
			while(Util.isEqualOrSmall(time2, to)
					&& Util.isEqualOrSmall(time2 - (from+duration), this.cycle))
			{
				double c = this.getRealCost(time2-duration, time2);
				if(Util.isSmall(c, cheapest2))
				{
					cheapest2 = c;
					cheapestFrom2 = time2 - duration;
				}
				time2 = this.getNextChangeTime(time2);
			}
			
			//merge
			if(cheapestFrom1 == null && cheapestFrom2 == null)
				return null;
			else if(cheapestFrom1 != null && cheapestFrom2 == null)
				return cheapestFrom1;
			else if(cheapestFrom1 == null && cheapestFrom2 != null)
				return cheapestFrom2;
			else if(Util.isEqual(cheapest1, cheapest2))
			{
				return Math.min(cheapestFrom1, cheapestFrom2);
			}
			else
				return (Util.isSmall(cheapest1, cheapest2) ? cheapestFrom1 : cheapestFrom2);
		}

		@Override
		public double getInitialCost() {
			return cost1;
		}
}
